/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 *
 *           File:  main.cpp
 *    Description:  Calculate CRC32 using SystemC data types
 *       
 *        Created:  16.02.2013 16:44:38
 *         Author:  Aleksey Golovchenko (lexgolovchenko@gmail.com)
 *   Organization:  St.Petersburg State Polytecnical Universisy
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <systemc.h>

#include "../cpp_templates/table_gen.h"
#include "crc32.h"

/*
 * Random number distribution
 */
#define RANDU ((double)rand())/RAND_MAX
int err_cntr;

/*
 * Data frame
 */
const size_t FRAME_WORDS_NUM = 16;
sc_uint<32> frame[FRAME_WORDS_NUM];  

// CRC32 table
crc32table<0xEDB88320UL> crc32tbl;

// Array for check CRC
uint32_t check_array[FRAME_WORDS_NUM];

// print frame to stdout
void display_frame() 
{
    for (int i = 0; i < FRAME_WORDS_NUM; ++i) {
        printf("%-9.8X", frame[i].to_uint());
    } 
    printf("\n");
}

// prepare frame and calculate CRC-code
void prepare_frame() 
{
    for (int i = 0; i < FRAME_WORDS_NUM - 1; ++i) {
        check_array[i] = rand();
        frame[i] = check_array[i];
    } 

    // SystemC CRC 
    sc_uint<32> crc = 0xFFFFFFFF;
    for (int i = 0; i < FRAME_WORDS_NUM - 1; ++i) {       
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(7, 0)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(15, 8)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(23, 16)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(31, 24)) & 0xFF];
    } 
    frame[FRAME_WORDS_NUM - 1] = crc ^ 0xFFFFFFFF;    

    // check crc
    uint32_t crc_check = crc32(check_array, 4*(FRAME_WORDS_NUM - 1)); 

    printf("crc = 0x%8.8X, check = 0x%8.8X \n", crc.to_uint() ^ 0xFFFFFFFF, crc_check); 

    display_frame();
}

// add noise
void add_noise(double p) 
{
    sc_uint<32> err;
    for (int i = 0; i < FRAME_WORDS_NUM; ++i) {        

        err = 0;        
        for (int j = 0; j < 32; ++j) {
            err[j] = (RANDU < p);
            err_cntr += err[j];
        }

        printf("%-9.8X", err.to_uint());

        frame[i] ^= err;
    } 
    printf("\n");
}

// check crc
int check_crc() 
{
    sc_uint<32> crc = 0xFFFFFFFF;
    for (int i = 0; i < FRAME_WORDS_NUM - 1; ++i) {       
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(7, 0)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(15, 8)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(23, 16)) & 0xFF];
        crc = (crc >> 8) ^ crc32tbl[(crc ^ frame[i].range(31, 24)) & 0xFF];
    } 
    crc ^= 0xFFFFFFFF;    

    display_frame();
    
    printf("Check CRC : %-9.8X \n", crc.to_uint());

    return crc == frame[FRAME_WORDS_NUM - 1];
}

int sc_main(int argc, char **argv)
{    
    if (argc < 2) {
        printf("Usage : %s err_probability \n", argv[0]);
        return -1;
    }

    // error probability
    double p = atof(argv[1]);

    srand(time(NULL));

    // prepare frame
    prepare_frame();  

    // add uniform random noise (binary simmetric channel)
    add_noise(p);    

    // check CRC-code
    if (!check_crc()) {
        printf("Wrong frame! Error counter %d \n", err_cntr);
    }

    return 0;
}
